package modelo.carrera;


import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Observable;

import compartido.IComparadorDeTransacciones;
import compartido.IEntidad;
import simulacion.control.ActivarTransaccionesSinDormirlas;
import simulacion.control.IPlanificador;
import simulacion.control.PrimerRegistradoMayorPrioridad;

import modelo.carrera.comparadores.JugadorPuestoComparador;
import modelo.carrera.comparadores.JugadorPuntoComparador;
import modelo.comportamiento.Accion;
import modelo.comportamiento.acciones.Acelerar;
import modelo.controles.Control;
import modelo.escenario.Ciudad;
import modelo.geometria.Vector;
import modelo.seniales.Semaforo;
import modelo.vehiculos.Vehiculo;

public class Carrera extends Observable implements IEntidad{
	protected static long maxIteraciones = 200;
	protected static int puntosDeLicenciaIniciales = 50;
	
	private long id;

	private Ciudad escenario;
	private Date horarioLargada;
	private Vector puntoLargada;
	private Vector puntoLlegada;
	private ArrayList<Jugador> jugadores;
	
	private boolean finalizada;
	private IPlanificador planificador;

	protected IPlanificador armarPlanificador() {
		PrimerRegistradoMayorPrioridad comparador = new PrimerRegistradoMayorPrioridad();
		comparador.registrar(Vehiculo.class);
		IPlanificador planificador= new ActivarTransaccionesSinDormirlas();
		planificador.setOrden(comparador);
		
		return planificador;
	}
	
	
	public Carrera(long identificador, Ciudad escenario, Date horarioLargada, Vector puntoLargada, Vector puntoLlegada)
	{
		this.id = identificador;
		this.escenario = escenario;
		this.horarioLargada = horarioLargada;
		this.puntoLargada = puntoLargada;
		this.puntoLlegada = puntoLlegada;
		this.finalizada = false;
		this.jugadores = new ArrayList<Jugador>();
		this.planificador = armarPlanificador();
	}

	public enum Eventos {
		InicioDeCarrera,
		SiguienteInstante,
		FinDeCarrera
	}
	
	public class EventoConcreto {
		protected Eventos tipo;
		
		public EventoConcreto(Eventos tipo) {
			this.tipo = tipo;
		}
		
		public Eventos getTipo() {
			return tipo;
		}
	}
	
	public void iniciarCarrera(){
		
		registrarEscenarioEnPlanificador();

		setChanged();
		notifyObservers(new EventoConcreto(Eventos.InicioDeCarrera));
		long iteracion = 0;
		while ( !finCarrera() && (iteracion < maxIteraciones) )
		{
			planificador.siguienteInstante();
			analizarJugadores();
			
	/*		Iterable<Vehiculo> vehiculos = getEscenario().getVehiculos();
			for (Vehiculo v : vehiculos) {
				v.activar();
			}
			/*Accion acelerar = new Acelerar(70, 100);
			v.manejar(acelerar); */
		
			setChanged();
			notifyObservers(new EventoConcreto(Eventos.SiguienteInstante));
			iteracion++;
		}
		finalizarCarrera();
		setChanged();
		notifyObservers(new EventoConcreto(Eventos.FinDeCarrera));
	}
	
	private boolean finCarrera(){
		finalizada = !planificador.tieneSiguienteInstante() || !hayJugadoresActivos();
		return finalizada;
	}

	private boolean hayJugadoresActivos(){
		for (Jugador jugador : jugadores){
			if (jugador.isActivo())
			return true;
		}
		return false;
	}
	
	private void analizarJugadores(){
		
		for (Jugador jugador: jugadores)
		{
			if (jugador.isActivo()){
				jugador.actualizar();
				
				if (jugador.getVehiculo().getConductor().getPuntos() == 0 || jugador.finalizoCarrera())
					planificador.remover(jugador.getVehiculo());
			}
		}
	}
	
	private void registrarEscenarioEnPlanificador(){

		if (escenario != null){
			for (Control control : escenario.getControles())
				planificador.registrar(control);
		
			for (Semaforo semaforo : escenario.getSemaforos())
				planificador.registrar(semaforo);
			
			for (Vehiculo vehiculo : escenario.getVehiculos())
				planificador.registrar(vehiculo);
		}
	}
	
	private void finalizarCarrera()
	{
		calcularPuestoLlegada();
	}

	private void calcularPuestoLlegada(){
		int numeroPuesto = 0;
		
		Collections.sort(jugadores, new JugadorPuntoComparador());
		for (Jugador jugador: jugadores){
			if (jugador.getPuntos()!= 0){
			   numeroPuesto += 1;
			   jugador.setPuesto(numeroPuesto);
			}
		}
		
    	Collections.sort(jugadores, new JugadorPuestoComparador());
    	for (Jugador jugador: jugadores){
			if (jugador.getPuesto() == 0){
				 numeroPuesto += 1;
				 jugador.setPuesto(numeroPuesto);
			}
		}
	}

	public void agregarJugador(Jugador jugador)
	{
		jugador.setCarrera(this);
		jugador.getVehiculo().getConductor().setPuntos(puntosDeLicenciaIniciales);
		jugadores.add(jugador);
		escenario.addObstaculo(jugador.getVehiculo());
		planificador.registrar(jugador.getVehiculo());
		
	}

	public Date getHorarioLargada() {
		return horarioLargada;
	}
	
	public boolean getFinalizada() {
		return finalizada;
	}
	
	public Ciudad getEscenario(){
		return escenario;
	}
	
	public ArrayList<Jugador> getJugadores(){
		return jugadores;
	}

	@Override
	public String getIdentificador() {
		return String.valueOf(id);
	}

	public Vector getPuntoLargada() {
		return puntoLargada;
	}
	
	public Vector getPuntoLLegada() {
		return puntoLlegada;
	}

}
